/**************************************************************************************
 * Copyright (c) 2018, Tomoaki Yamaguchi
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 *
 * The Eclipse Public License is available at
 *    http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *    Tomoaki Yamaguchi - initial API and implementation and/or initial documentation
 **************************************************************************************/

#include "MQTTSNGWMessageIdTable.h"
#include "MQTTSNGWClient.h"

using namespace MQTTSNGW;

/*===============================
 * Class MessageIdTable
 ===============================*/
MessageIdTable::MessageIdTable()
{

}

MessageIdTable::~MessageIdTable()
{
    _mutex.lock();
    if (_head != nullptr)
    {
        MessageIdElement* p = _tail;
        while (p)
        {
            MessageIdElement* pPrev = p;
            delete p;
            _cnt--;
            p = pPrev->_prev;
        }
        _head = _tail = nullptr;
    }
    _mutex.unlock();
}

MessageIdElement* MessageIdTable::add(Aggregater* aggregater, Client* client, uint16_t clientMsgId)
{
    if (_cnt > _maxSize)
    {
        return nullptr;
    }

    MessageIdElement* elm = new MessageIdElement(0, client, clientMsgId);
    if (elm == nullptr)
    {
        return nullptr;
    }
    _mutex.lock();
    if (_head == nullptr)
    {
        elm->_msgId = aggregater->msgId();
        _head = elm;
        _tail = elm;
        _cnt++;
    }
    else
    {
        MessageIdElement* p = find(client, clientMsgId);
        if (p == nullptr)
        {
            elm->_msgId = aggregater->msgId();
            p = _tail;
            _tail = elm;
            elm->_prev = p;
            p->_next = elm;
            _cnt++;
        }
        else
        {
            delete elm;
            elm = nullptr;
        }
    }
    _mutex.unlock();
    return elm;
}

MessageIdElement* MessageIdTable::find(uint16_t msgId)
{
    MessageIdElement* p = _head;
    while (p)
    {
        if (p->_msgId == msgId)
        {
            break;
        }
        p = p->_next;
    }
    return p;
}

MessageIdElement* MessageIdTable::find(Client* client, uint16_t clientMsgId)
{
    MessageIdElement* p = _head;
    while (p)
    {
        if (p->_clientMsgId == clientMsgId && p->_client == client)
        {
            break;
        }
        p = p->_next;
    }
    return p;
}

Client* MessageIdTable::getClientMsgId(uint16_t msgId, uint16_t* clientMsgId)
{
    Client* clt = nullptr;
    *clientMsgId = 0;
    _mutex.lock();
    MessageIdElement* p = find(msgId);
    if (p != nullptr)
    {
        clt = p->_client;
        *clientMsgId = p->_clientMsgId;
        clear(p);
    }
    _mutex.unlock();
    return clt;
}

void MessageIdTable::erase(uint16_t msgId)
{
    _mutex.lock();
    MessageIdElement* p = find(msgId);
    clear(p);
    _mutex.unlock();
}

void MessageIdTable::clear(MessageIdElement* elm)
{
    if (elm == nullptr)
    {
        return;
    }

    if (elm->_prev == nullptr)
    {
        _head = elm->_next;
        if (_head == nullptr)
        {
            _tail = nullptr;
        }
        else
        {
            _head->_prev = nullptr;
        }
        delete elm;
        _cnt--;
        return;
    }
    else
    {
        elm->_prev->_next = elm->_next;
        if (elm->_next == nullptr)
        {
            _tail = elm->_prev;
        }
        else
        {
            elm->_next->_prev = elm->_prev;
        }
        delete elm;
        _cnt--;
        return;
    }
}

uint16_t MessageIdTable::getMsgId(Client* client, uint16_t clientMsgId)
{
    uint16_t msgId = 0;
    MessageIdElement* p = find(client, clientMsgId);
    if (p != nullptr)
    {
        msgId = p->_msgId;
    }
    return msgId;
}

/*===============================
 * Class MessageIdElement
 ===============================*/
MessageIdElement::MessageIdElement(void) :
        _msgId { 0 }, _clientMsgId { 0 }, _client { nullptr }, _next { nullptr }, _prev { nullptr }
{

}

MessageIdElement::MessageIdElement(uint16_t msgId, Client* client, uint16_t clientMsgId) :
        MessageIdElement()
{
    _msgId = msgId;
    _client = client;
    _clientMsgId = clientMsgId;
}

MessageIdElement::~MessageIdElement(void)
{

}
